#include "vtor_iap.h"
#include "vtor_timer.h"
#include "vtor_printf.h"
#include "vtor_flash_eeprom.h"
#include "main.h"

#ifdef __VTOR_IAP__
// iap共有数据结构
uint8_t iapFlashBuf[VtorIapBufSize + 10] = {0};

VtorFlashEeprom vtorIapEeprom = 
{
	.flashStartAddress = EepromFlashAddr,
	.flashPageSize = VtorFlashPageSize,
	.flashPageCnt = EepromFlashPageCnt,
	.eepromSize = EepromDataSize,
};

// 被存储在eeprom的数据（版本信息等）
VtorIapEepromData iapEepromData = {0};
#endif // __VTOR_IAP__



#ifdef __VTOR_IAP_APP__

VtorIap vtorIap = 
{
	.iapStartFlag = 0,
	.iapCurRxCnt = 0,
	.iapTotalRxCnt = 0,
	.iapStartAddr = BAK_ADDR, // 存储的地址
	.iapTotalSize = (BAK_ADDR - APP_ADDR),
	.iapBuf = iapFlashBuf,
	.iapBufSize = VtorIapBufSize,
	.iapCrc = 0,
};


// 系统复位  所有电路都会复位
void VtorIap_Reset(void)
{
	// 仅在cortex m3上测试成功
	__set_FAULTMASK(1);	 //关闭所有中断
	// 源码见 core_cm3.h 文件 static __INLINE void NVIC_SystemReset(void) 函数
	NVIC_SystemReset();  //系统复位
}


uint32_t VtorIap_EraseEerpom()
{
	return VtorFlashEeprom_Erase(&vtorIapEeprom);
}

uint32_t VtorIap_WriteFlash()
{
	// 往flash里面写数据
	uint32_t flashAddr = vtorIap.iapStartAddr + vtorIap.iapTotalRxCnt;
	// 保证sumcrc不出问题
	vtorIap.iapBuf[vtorIap.iapCurRxCnt + 0] = '\0';
	vtorIap.iapBuf[vtorIap.iapCurRxCnt + 1] = '\0';
	vtorIap.iapBuf[vtorIap.iapCurRxCnt + 2] = '\0';
	vtorIap.iapBuf[vtorIap.iapCurRxCnt + 3] = '\0';
	
	// 调整大小，使其正确写入
	vtorIap.iapCurRxCnt += 3;
	vtorIap.iapCurRxCnt &= ~(3);
	
	vtorIap.iapTotalRxCnt += vtorIap.iapCurRxCnt;
	
	int writeRet
		= VtorFlash_Write(flashAddr, 
		(uint32_t*)vtorIap.iapBuf,
		vtorIap.iapCurRxCnt);
	
	for(int i = 0; i< vtorIap.iapCurRxCnt; i++)
	{
		vtorIap.iapCrc += vtorIap.iapBuf[i];
	}
	// 成功返回地址，失败返回0
	return writeRet;
}


uint32_t VtorIap_SetNewVersion(int newVersion)
{
#ifdef __VTOR_IAP_CONFIG_SAFE_IAP__
	if(0 == iapEepromData.newFile.fileSize
		|| '\0' == iapEepromData.newFile.fileName[0])
	{
		return iapEepromData.curFile.fileVersion;
	}
#endif
	VtorFlashEeprom_Read(&vtorIapEeprom, &iapEepromData, sizeof(iapEepromData));
	
	// 使用 curSoftVersion 比较
	// 允许设置成相等，相等时不会进行升级
	if(newVersion >= iapEepromData.curFile.fileVersion)
	{
		VtorDebugChanPrintf("[%d] ver %d -> %d\r\n",
			iapEepromData.idx,
			iapEepromData.curFile.fileVersion,
			newVersion);
		
		// 使用 newSoftVersion 记录，iap中进行比较，再升级
		iapEepromData.newFile.fileVersion = newVersion;
		VtorFlashEeprom_Write(&vtorIapEeprom, &iapEepromData, sizeof(iapEepromData));
	}
	// 返回当前版本，那么输入 0 就是查询版本号
	return iapEepromData.curFile.fileVersion;
}


// 读大缓冲，用于iap升级的东西
void VtorIap_ReceiveDataTask()
{
	// 接收大文件，用于iap
	if(vtorIap.iapStartFlag)
	{

#ifdef __VTOR_IAP_STM32L431CB__
#include "vtor_i2c.h"
		extern VtorI2c* stc8I2c;
		vtorIap.iapCurRxCnt = VtorI2cMaster_ReadBigBuffer(stc8I2c,
			BUFFER_I2C_SLAVE_ADDR, vtorIap.iapBuf, vtorIap.iapBufSize);
#endif
		if(vtorIap.iapCurRxCnt)
		{
			// 不启用安全检测时，只检查flash边界
			//   启用安全检测时，同时检测文件大小与接收大小
#ifdef __VTOR_IAP_CONFIG_SAFE_IAP__
			if(vtorIap.iapTotalRxCnt + vtorIap.iapCurRxCnt
					> iapEepromData.newFile.fileSize
				|| vtorIap.iapTotalRxCnt + vtorIap.iapCurRxCnt
					> vtorIap.iapTotalSize)
#else
			if(vtorIap.iapTotalRxCnt + vtorIap.iapCurRxCnt
				> vtorIap.iapTotalSize)
#endif
			{
				VtorDebugChanPrintf("iap will cross, stop!\r\n");
				vtorIap.iapStartFlag = 0;
				VtorTimer_Delete(VtorIap_ReceiveDataTask);
				return ;
			}
			// 如果为0，表明写失败，停止接收
			uint32_t writeRet = VtorIap_WriteFlash();
			//VtorDebugChanPrintf(" %d %d\r\n", vtorIap.iapCurRxCnt, vtorIap.iapTotalRxCnt);
			if(writeRet)
			{
				VtorDebugChanPrintf("write 0x%x crc 0x%x size %d/%d success\r\n",
					writeRet, vtorIap.iapCrc, 
					vtorIap.iapCurRxCnt, vtorIap.iapTotalRxCnt);
			}
			else
			{
				VtorDebugChanPrintf("write fail crc 0x%x size %d/%d fail\r\n",
					vtorIap.iapCrc, 
					vtorIap.iapCurRxCnt, vtorIap.iapTotalRxCnt);
					// 失败，停止
					vtorIap.iapStartFlag = 0;
					VtorTimer_Delete(VtorIap_ReceiveDataTask);
			}
			// 写完后再清空
			vtorIap.iapCurRxCnt = 0;
		}
	}
}


// 启动命令，便开始擦除，记录文件名，文件大小
void VtorIap_StartIap(uint8_t* fileName, uint32_t fileSize)
{
#ifdef __VTOR_IAP_CONFIG_SAFE_IAP__
	if(0 == fileSize
		|| NULL == fileName
		|| '\0' == fileName[0])
	{
		return ;
	}
#endif
	
	// 使用文件名，文件大小
	VtorFlashEeprom_Read(&vtorIapEeprom, &iapEepromData, sizeof(iapEepromData));
	int32_t nameLen = VTOR_IAP_FILE_NAME_LEN;
	VtorString_AppendString(iapEepromData.newFile.fileName, &nameLen, fileName);
	iapEepromData.newFile.fileSize = fileSize;
	iapEepromData.newFile.fileVersion = 0; // 清零，防止误触发
	VtorFlashEeprom_Write(&vtorIapEeprom, &iapEepromData, sizeof(iapEepromData));
	
	// 根据情况，使用入参，或者使用初始值
	vtorIap.iapStartFlag = 1;
	vtorIap.iapCurRxCnt = 0;
	vtorIap.iapTotalRxCnt = 0;
	vtorIap.iapCrc = 0;
	
	uint32_t eraseRet = 0;
	uint32_t flashStartAddr = vtorIap.iapStartAddr;
	uint32_t pageCnt = (vtorIap.iapTotalSize + VtorFlashPageSize - 1) / VtorFlashPageSize;
	for(int i = 0; i < pageCnt; i++)
	{
		uint32_t flashAddr = flashStartAddr  + i * VtorFlashPageSize;
		eraseRet = VtorFlash_Erase(flashAddr, 1);
		if(0xffffffff == eraseRet)
		{
			VtorDebugChanPrintf("erase 0x%x success\r\n",flashAddr);
		}
		else
		{
			VtorDebugChanPrintf("erase 0x%x fail\r\n",flashAddr);
		}
	}
	VtorDebugChanPrintf("VtorIap_StartIap %s %d\r\n",
		iapEepromData.newFile.fileName,
		iapEepromData.newFile.fileSize);
	VtorTimer_Create(VtorIap_ReceiveDataTask, 10);
}


uint32_t VtorIap_StopIap(uint32_t crc)
{
	VtorDebugChanPrintf("VtorIap_StopIap 0x%x\r\n", crc);
	
	vtorIap.iapStartFlag = 0;
	VtorTimer_Delete(VtorIap_ReceiveDataTask);
#ifdef __VTOR_IAP_CONFIG_SAFE_IAP__
	if(crc != vtorIap.iapCrc
		|| iapEepromData.newFile.fileSize != vtorIap.iapTotalRxCnt)
	{
		VtorDebugChanPrintf("crc or size fail\r\n");
		
		VtorFlashEeprom_Read(&vtorIapEeprom,
			&iapEepromData, sizeof(iapEepromData));
		iapEepromData.newFile.fileSize = 0;
		iapEepromData.newFile.fileName[0] = '\0';
		VtorFlashEeprom_Write(&vtorIapEeprom,
			&iapEepromData, sizeof(iapEepromData));
	}
#endif
	return crc;
}



#endif // __VTOR_IAP_APP__

